/*==================================================================
 *
 *
 *  File name:  OLED.c
 *  Use to:     Function for drawing graphics and text on OLED
 *  Author:     Li Liming,  Hao Zhiyu
 *
 *  文件名：            Display.c
 *  用途：                 用于在OLED上绘制图形和文字的功能函数
 *  作者：                 李立明，郝志宇
 *
 *
==================================================================*/

#include "config.h"
#include "OLED.h"
#include "Display.h"

void OLED_WR_Byte(u8 dat,u8 cmd)
{
    u8 i;
    if(cmd)
      OLED_DC_1();
    else
      OLED_DC_0();
    SPI0_MasterSendByte(dat);
    OLED_DC_1();
}

void OLED_Set_Pos(unsigned char x, unsigned char y)
{
    OLED_WR_Byte(0xb0+y,OLED_CMD); // 设置页地址（0~7）
    OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD); // 设置列高4位地址
    OLED_WR_Byte((x&0x0f)|0x01,OLED_CMD); // 设置列低4位地址
}

// 打开OLED显示
void OLED_Display_On(void)
{
    OLED_WR_Byte(0X8D,OLED_CMD);  // 设置电荷泵
    OLED_WR_Byte(0X14,OLED_CMD);  // 电荷泵打开
    OLED_WR_Byte(0XAF,OLED_CMD);  // 显示打开
}

// 关闭OLED显示
void OLED_Display_Off(void)
{
    OLED_WR_Byte(0X8D,OLED_CMD);  // 设置电荷泵
    OLED_WR_Byte(0X10,OLED_CMD);  // 电荷泵关闭
    OLED_WR_Byte(0XAE,OLED_CMD);  // 显示关闭
}

// 清屏函数，清屏后屏幕全黑！注意清屏是整页清屏!!!
void OLED_Clear(void)
{
    u8 i,n;
    for(i=0;i<8;i++)
    {
        OLED_WR_Byte (0xb0+i,OLED_CMD);    // 设置页地址（0~7）
        OLED_WR_Byte (0x00,OLED_CMD);      // 设置列起始地址低4位
        OLED_WR_Byte (0x10,OLED_CMD);      // 设置列起始地址高4位
        for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA); // 清除一页内容
    } // 遍历所有页并清除
}

/*==================================================================
函数名：OLED_ShowChar
功能：     显示单个字符
形参：      u8 x:       所显示字符的x坐标
        u8 y:       所显示字符的y坐标
        u8 char:    单个字符内容
        u8 size:    字模大小选择      可选8*16 或6*8
        u8 neg:     是否反选                 0正常     1反选
==================================================================*/
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 neg)
{
    unsigned char c=0,i=0;
        c=chr-' ';// 用于对照字模表
        if(size ==16){      //选择大小为16的字模
            if(neg == 0){
                if(x>Max_Column - 1){x=0; y+=2;}
                OLED_Set_Pos(x,y);
                for(i=0;i<8;i++)
                    OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
                OLED_Set_Pos(x,y+1);
                for(i=0;i<8;i++)
                    OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
            }else{
                if(x>Max_Column - 1){x=0; y+=2;}
                OLED_Set_Pos(x,y);
                for(i=0;i<8;i++)
                    OLED_WR_Byte(~F8X16[c*16+i],OLED_DATA);
                OLED_Set_Pos(x,y+1);
                for(i=0;i<8;i++)
                    OLED_WR_Byte(~F8X16[c*16+i+8],OLED_DATA);
            }
        }
//        else if(size == 12){   //选择大小为12的字模
//            if(neg ==0){
//                if(x > Max_Column - 1){x = 0; y += 2;}
//                OLED_Set_Pos(x,y);
//                for(i=0;i<6;i++)
//                    OLED_WR_Byte(NUM6x12[c][i],OLED_DATA);
//                OLED_Set_Pos(x,y+1);
//                for(i=0;i<6;i++)
//                    OLED_WR_Byte(NUM6x12[c][i+6],OLED_DATA);
//            }else{
//                if(x > Max_Column - 1){x = 0; y += 2;}
//                OLED_Set_Pos(x,y);
//                for(i=0;i<6;i++)
//                    OLED_WR_Byte(~NUM6x12[c][i],OLED_DATA);
//                OLED_Set_Pos(x,y+1);
//                for(i=0;i<6;i++)
//                    OLED_WR_Byte(~NUM6x12[c][i+6],OLED_DATA);
//            }
//        }
        else{                  //选择大小为8的字模
            if(neg == 0){
                if(x > Max_Column - 1){x = 0; y += 1;}
                OLED_Set_Pos(x,y+1);
                for(i=0;i<6;i++)
                    OLED_WR_Byte(F6x8[c][i],OLED_DATA);
            }else{
                if(x > Max_Column - 1){x = 0; y += 1;}
                OLED_Set_Pos(x,y+1);
                for(i=0;i<6;i++)
                    OLED_WR_Byte(~F6x8[c][i],OLED_DATA);
            }

        }
}
/*==================================================================
函数名：OLED_ShowString
功能：     显示多个字符
形参：      u8 x:       所显示字符的x坐标
        u8 y:       所显示字符的y坐标
        u8 *chr:    字符数组指针
        u8 size:    字模大小选择      可选8*16 或6*8
        u8 neg:     是否反选                 0正常     1反选
==================================================================*/
void OLED_ShowString(u8 x, u8 y, u8 *chr, u8 size, u8 neg)
{
    unsigned char j=0; // 从参数*chr中读取字符
    while (chr[j]!='\0')
    {       OLED_ShowChar(x,y,chr[j],size, neg);
            x+=size;
        if(x>122){x=0;y+=2;}
            j++;
    }
}
/*==================================================================
函数名：OLED_ShowMainString
功能：     专供测量界面使用显示8*16大小的数字字符
形参：      u8 x:       所显示字符的x坐标
        u8 y:       所显示字符的y坐标
        u8 *chr:    字符数组指针
        u8 size:    字模大小选择      可选8*16 或6*8
        u8 neg:     是否反选                 0正常     1反选
==================================================================*/
void OLED_ShowMainString(u8 x,u8 y,u8 *chr,u8 size,u8 neg)
{
    unsigned char j=0; // 从参数*chr中读取字符
    while (chr[j]!='\0')
    {       OLED_ShowChar(x,y,chr[j],size, neg);
            x+=8;
        if(x>122){x=0;y+=2;}
            j++;
    }
}

/*==================================================================
函数名：OLED_ShowCHinese
功能：     显示单个汉字，字模大小12*12
形参：      u8 x:       所显示字符的x坐标
        u8 y:       所显示字符的y坐标
        u8 no:      从字模库选择序号
        u8 neg:     是否反选                 0正常     1反选
==================================================================*/
void OLED_ShowCHinese(u8 x, u8 y, u8 no, u8 neg)
{
    u8 t, i, n = 0;
    if (neg == 0) { // 如果不需要取反显示
        for (t = 0; t < 2; t++) {
            OLED_Set_Pos(x, y + t);  // 设置显示位置

            // 遍历汉字的每一行（每行12字节）
            for (i = 0; i < 12; i++) {
                OLED_WR_Byte(hanzi[no][n], OLED_DATA);  // 写入汉字数据
                n++;
            }
        }
    } else {  // 如果需要取反显示
        for (t = 0; t < 2; t++) {
            OLED_Set_Pos(x, y + t);  // 设置显示位置

            if(t == 1)
            {
                for (i = 0; i < 12; i++)
                {
                    OLED_WR_Byte(0x1F ^ hanzi[no][n], OLED_DATA);  // 写入汉字数据并取反前5行
                    n++;
                }
            }else{
                for (i = 0; i < 12; i++) {
                    OLED_WR_Byte(~hanzi[no][n], OLED_DATA);  // 写入汉字数据并取反
                    n++;
                }
            }
            // 遍历汉字的每一行（每行12字节），并取反显示

        }
    }
}

/*==================================================================
函数名：OLED_ShowChineseString
功能：     显示词组汉字，字模大小12*12，自定义词组数组，在Display.c和.h中加入
形参：      u8 x:       所显示字符的x坐标
        u8 y:       所显示字符的y坐标
        unsigned char cizu[][24]: 准备好的词组数组
        u8 len:     词组数组长度
        u8 neg:     是否反选                 0正常     1反选
==================================================================*/
void OLED_ShowChineseString(u8 x, u8 y, unsigned char cizu[][24], u8 len, u8 neg) {
    u8 k, t, i, n;
    if(neg == 0)
    {
        for (k = 0; k < len; k++) { // 遍历每个字符
            n = 0;
            for (t = 0; t < 2; t++) { // 分两部分（每部分8行）
                OLED_Set_Pos(x + k * 12, y + t); // 设置光标位置
                for (i = 0; i < 12; i++) { // 每部分12列
                    OLED_WR_Byte(cizu[k][n], OLED_DATA); // 输出字模数据
                    n++;
                }
            }
        }
    }else{
        for (k = 0; k < len; k++) { // 遍历每个字符
            n = 0;
            for (t = 0; t < 2; t++) { // 分两部分（每部分8行）
                OLED_Set_Pos(x + k * 12, y + t); // 设置光标位置

                if(t==1){
                    for (i = 0; i < 12; i++)
                    { // 每部分12列
                        OLED_WR_Byte(0x1F ^ cizu[k][n], OLED_DATA); // 输出取反字模数据
                        n++;
                    }
                }else{
                    for (i = 0; i < 12; i++)
                    { // 每部分12列
                        OLED_WR_Byte(~cizu[k][n], OLED_DATA); // 输出取反字模数据
                        n++;
                    }
                }
            }
        }
    }

}
/*==================================================================
函数名：OLED_ShowFuhao
功能：     用于显示符号和数字0123456789:, 字模大小5*12
形参：      u8 x:       所显示字符的x坐标
        u8 y:       所显示字符的y坐标
        u8 no:      数字或符号
==================================================================*/
void OLED_ShowFuhao(u8 x,u8 y,u8 no)//
{
    u8 t, i, n = 0;
    // 高度循环 (2 行)
    for (t = 0; t < 2; t++) { // 字模高度为 2 行
        OLED_Set_Pos(x, y + t); // 设置起始位置 (从 y 开始的第 t 页)
        // 宽度循环 (5 列)
        for (i = 0; i < 6; i++) { // 每行宽度为 5
            OLED_WR_Byte(fuhao[no][n], OLED_DATA); // 从字模数据中写入一字节
            n++; // 读取下一个字节
        }
    }
}
/*==================================================================
函数名：OLED_BigNum
功能：     用于显示主界面大号数字
形参：      u8 x:       所显示字符的x坐标
        u8 y:       所显示字符的y坐标
        u8 no:      数字或符号
==================================================================*/
void OLED_BigNum(u8 x,u8 y,u8 no)//
{
    u8 t,i,n=0;
    for (t = 0; t < 6; t++) {
        OLED_Set_Pos(x,y+t);
        for (i = 0; i < 24; i++) {
            OLED_WR_Byte(Num24x48[no][n],OLED_DATA);
            n++;
        }
    }
}

void OLED_ChineseNum(u8 x, u8 y, u8 num, u8 neg){
    u8 t,i,n=0;
    if(neg ==0){
        for(t = 0; t< 2; t++){
            OLED_Set_Pos(x, y+t);
            for(i = 0; i<6; i++){
                OLED_WR_Byte(NUM6x12[num][n], OLED_DATA);
                n++;
            }
        }
    }else{
        for(t = 0; t< 2; t++){
            OLED_Set_Pos(x, y+t);
            if(t == 1){
                for(i = 0; i<6; i++){
                    OLED_WR_Byte(0x0F ^ NUM6x12[num][n], OLED_DATA);
                    n++;
                }
            }else{
                for(i = 0; i<6; i++){
                    OLED_WR_Byte(~NUM6x12[num][n], OLED_DATA);
                    n++;
                }
            }

        }
    }

}

/*==================================================================
函数名：OLED_DrawBMP
功能：     绘制BMP图像，适用于128x64分辨率的OLED显示屏
形参：      x0, y1:     图像左上角坐标 (0~127, 0~63)
        x1, y1:     图像右下角坐标 (0~127, 0~63)
        BMP[]:      存储BMP图像数据的数组，数组中的数据会被按行写入显示器
==================================================================*/
void OLED_DrawBMP(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char BMP[])
{
    unsigned int j = 0;  // 用于BMP数据数组的索引
    unsigned char x, y;

    // 计算需要的行数，y1为图像的高度
    if (y1 % 8 == 0)
        y = y1 / 8;  // 如果高度能被8整除
    else
        y = y1 / 8 + 1;  // 否则需要多一行来显示

    // 遍历y轴上的每一行
    for (y = y0; y < y1; y++)
    {
        OLED_Set_Pos(x0, y);  // 设置OLED显示的起始位置为（x0, y）

        // 遍历x轴上的每一列
        for (x = x0; x < x1; x++)
        {
            OLED_WR_Byte(BMP[j++], OLED_DATA);  // 从BMP数据中读取并显示对应的数据
        }
    }
}

/*==================================================================
函数名：OLED_Init
功能：     初始化OLED显示屏（SSD1306）
==================================================================*/
void OLED_Init(void)
{
    OLED_RST_1();  // 设定复位引脚为高
    DelayMs(100);  // 延时100ms
    OLED_RST_0();  // 设定复位引脚为低
    DelayMs(100);  // 延时100ms
    OLED_RST_1();  // 设定复位引脚为高

    OLED_WR_Byte(0xAE, OLED_CMD);  // 关闭OLED显示屏
    OLED_WR_Byte(0x00, OLED_CMD);  // 设置低位列地址
    OLED_WR_Byte(0x10, OLED_CMD);  // 设置高位列地址
    OLED_WR_Byte(0x40, OLED_CMD);  // 设置起始行地址，设置RAM显示开始行（0x00~0x3F）

    OLED_WR_Byte(0x81, OLED_CMD);  // 设置对比度控制寄存器
    OLED_WR_Byte(0x50, OLED_CMD);  // 设置SEG输出电流亮度

    OLED_WR_Byte(0xA1, OLED_CMD);  // 设置SEG/列映射（0xA0为默认映射，0xA1为翻转映射）
    OLED_WR_Byte(0xC8, OLED_CMD);  // 设置COM/行扫描方向（0xC0为默认扫描方向，0xC8为翻转扫描方向）

    OLED_WR_Byte(0xA6, OLED_CMD);  // 设置正常显示模式（0xA7为反色显示模式）

    OLED_WR_Byte(0xA8, OLED_CMD);  // 设置复用率（1~64）
    OLED_WR_Byte(0x3F, OLED_CMD); // 设置为1/64复用率

    OLED_WR_Byte(0xD3, OLED_CMD);  // 设置显示偏移量
    OLED_WR_Byte(0x00, OLED_CMD);  // 不偏移

    OLED_WR_Byte(0xD5, OLED_CMD);  // 设置显示时钟分频比/振荡器频率
    OLED_WR_Byte(0x00, OLED_CMD);  // 设置分频比，显示时钟为100帧/秒

    OLED_WR_Byte(0xD9, OLED_CMD);  // 设置预充电期
    OLED_WR_Byte(0xF1, OLED_CMD);  // 设置预充电时间为15时钟周期，放电时间为1时钟周期

    OLED_WR_Byte(0xDA, OLED_CMD);  // 设置COM引脚硬件配置
    OLED_WR_Byte(0x12, OLED_CMD);  // 配置为顺序扫描模式

    OLED_WR_Byte(0xDB, OLED_CMD);  // 设置VCOMH电压
    OLED_WR_Byte(0x40, OLED_CMD);  // 设置VCOM选择电平

    OLED_WR_Byte(0x20, OLED_CMD);  // 设置页地址模式
    OLED_WR_Byte(0x02, OLED_CMD);  // 设置为页地址模式

    OLED_WR_Byte(0x8D, OLED_CMD);  // 设置电荷泵使能/禁用
    OLED_WR_Byte(0x14, OLED_CMD);  // 启用电荷泵（0x10禁用，0x14启用）

    OLED_WR_Byte(0xA4, OLED_CMD);  // 禁用全屏显示（0xA4表示正常显示，0xA5表示全屏显示）

    OLED_WR_Byte(0xA6, OLED_CMD);  // 禁用反向显示（0xA6表示正常显示，0xA7表示反向显示）

    OLED_WR_Byte(0xAF, OLED_CMD);  // 开启OLED显示面板
    OLED_WR_Byte(0xAF, OLED_CMD);  // 开启显示

    OLED_Clear();  // 清屏
    OLED_Set_Pos(0, 0);  // 设置光标位置为（0，0）
}

